iT邦幫忙

2024 iThome 鐵人賽

DAY 20
0
Modern Web

React 學得動嗎系列 第 20

[Day 20] Gym Pro:設計登入頁面和主導航,整合 React Query

  • 分享至 

  • xImage
  •  

今天,我們要為 Gym Pro 設計登入頁面和主要的導航結構,並討論如何使用 React Query 來優化我們的 API 請求。

1. 設計登入頁面

首先,我們將設計一個簡潔且具吸引力的登入頁面,並使用 shadcn/ui 的組件來快速建立 UI。

src/pages/Login.tsx 中:

import React from 'react';
import { useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card"

interface LoginForm {
  email: string;
  password: string;
}

const Login: React.FC = () => {
  const { register, handleSubmit } = useForm<LoginForm>();
  const navigate = useNavigate();

  const onSubmit = (data: LoginForm) => {
    console.log(data);
    // 這裡應該處理登入邏輯
    // 成功後導航到儀表板
    navigate('/dashboard');
  };

  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-100">
      <Card className="w-[350px]">
        <CardHeader>
          <CardTitle>歡迎來到 Gym Pro</CardTitle>
          <CardDescription>請登入以繼續</CardDescription>
        </CardHeader>
        <CardContent>
          <form onSubmit={handleSubmit(onSubmit)}>
            <div className="space-y-4">
              <Input {...register('email')} type="email" placeholder="Email" required />
              <Input {...register('password')} type="password" placeholder="密碼" required />
            </div>
            <Button className="w-full mt-4" type="submit">登入</Button>
          </form>
        </CardContent>
        <CardFooter>
          <p className="text-sm text-gray-500">還沒有帳號?請聯繫管理員</p>
        </CardFooter>
      </Card>
    </div>
  );
};

export default Login;

2. 設計主導航

接下來,我們來更新我們的主要布局,包括側邊導航欄和頂部的用戶頭像。

src/layouts/MainLayout.tsx 中:

import React from 'react';
import { Link, useLocation } from 'react-router-dom';
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"

const navItems = [
  { path: '/dashboard', label: '儀表板' },
  { path: '/members', label: '會員管理' },
  { path: '/classes', label: '課程管理' },
  { path: '/reports', label: '報表' },
];

interface MainLayoutProps {
  children: React.ReactNode;
}

const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
  const location = useLocation();

  return (
    <div className="flex h-screen bg-gray-100">
      <nav className="w-64 bg-white shadow-md">
        <div className="p-4">
          <h1 className="text-2xl font-bold text-gray-800">Gym Pro</h1>
        </div>
        <ul className="space-y-2 p-4">
          {navItems.map((item) => (
            <li key={item.path}>
              <Link to={item.path}>
                <Button 
                  variant="ghost" 
                  className={cn(
                    "w-full justify-start",
                    location.pathname === item.path && "bg-gray-100"
                  )}
                >
                  {item.label}
                </Button>
              </Link>
            </li>
          ))}
        </ul>
      </nav>
      <main className="flex-1 overflow-y-auto">
        <header className="bg-white shadow-sm">
          <div className="max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8 flex justify-between items-center">
            <h2 className="text-xl font-semibold text-gray-800">
              {navItems.find(item => item.path === location.pathname)?.label}
            </h2>
            <Avatar>
              <AvatarImage src="https://github.com/shadcn.png" alt="使用者" />
              <AvatarFallback>CN</AvatarFallback>
            </Avatar>
          </div>
        </header>
        <div className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
          {children}
        </div>
      </main>
    </div>
  );
};

export default MainLayout;

3. 整合 React Query

React Query 是一個強大的狀態管理工具,特別適合處理伺服器數據。我們將利用 React Query 來優化 API 請求的管理。

首先,安裝 React Query:

npm install @tanstack/react-query

然後,在 src/main.tsx 中設置 React Query:

import React from 'react'
import ReactDOM from 'react-dom/client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import App from './App.tsx'
import './index.css'

const queryClient = new QueryClient()

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </React.StrictMode>,
)

接下來,建立一個 src/hooks/useUser.ts 來獲取用戶資料:

import { useQuery } from '@tanstack/react-query';
import axios from 'axios';

interface User {
  id: number;
  name: string;
  email: string;
  avatar: string;
}

const fetchUser = async (): Promise<User> => {
  const { data } = await axios.get<User>('/api/user');
  return data;
};

export const useUser = () => {
  return useQuery(['user'], fetchUser);
};

現在,我們可以在任何組件中使用這個 hook 來獲取用戶資料。例如,在 MainLayout 中:

import { useUser } from '@/hooks/useUser';

// 在組件內部
const { data: user, isLoading } = useUser();

// 在 Avatar 組件中
<Avatar>
  {isLoading ? (
    <AvatarFallback>載入中...</AvatarFallback>
  ) : (
    <>
      <AvatarImage src={user?.avatar} alt={user?.name} />
      <AvatarFallback>{user?.name?.[0]}</AvatarFallback>
    </>
  )}
</Avatar>

小結

今天我們完成了:

  1. 設計了一個簡潔的登入頁面
  2. 建立了一個功能齊全的主導航布局,包括側邊欄和頂部導航
  3. 整合了 React Query 來處理 API 請求

上一篇
[Day 19] Gym Pro:健身房後台管理系統實作
下一篇
[Day 21] Gym Pro:實作會員管理功能
系列文
React 學得動嗎30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言